home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright (c) 1992 David I. Bell
- * Permission is granted to use, distribute, or modify this source,
- * provided that this copyright notice remains intact.
- *
- * Routines to do modulo arithmetic both normally and also using the REDC
- * algorithm given by Peter L. Montgomery in Mathematics of Computation,
- * volume 44, number 170 (April, 1985). For multiple multiplies using
- * the same large modulus, the REDC algorithm avoids the usual division
- * by the modulus, instead replacing it with two multiplies or else a
- * special algorithm. When these two multiplies or the special algorithm
- * are faster then the division, then the REDC algorithm is better.
- */
-
- #include "xmath.h"
-
-
- #define POWBITS 4 /* bits for power chunks (must divide BASEB) */
- #define POWNUMS (1<<POWBITS) /* number of powers needed in table */
-
-
- LEN _pow2_ = POW_ALG2; /* modulo size to use REDC for powers */
- LEN _redc2_ = REDC_ALG2; /* modulo size to use second REDC algorithm */
-
- static REDC *powermodredc = NULL; /* REDC info for raising to power */
-
- #if 0
- extern void zaddmod proto((ZVALUE z1, ZVALUE z2, ZVALUE z3, ZVALUE *res));
- extern void znegmod proto((ZVALUE z1, ZVALUE z2, ZVALUE *res));
-
- /*
- * Multiply two numbers together and then mod the result with a third number.
- * The two numbers to be multiplied can be negative or out of modulo range.
- * The result will be in the range 0 to the modulus - 1.
- */
- void
- zmulmod(z1, z2, z3, res)
- ZVALUE z1; /* first number to be multiplied */
- ZVALUE z2; /* second number to be multiplied */
- ZVALUE z3; /* number to take mod with */
- ZVALUE *res; /* result */
- {
- ZVALUE tmp;
- FULL prod;
- FULL digit;
- BOOL neg;
-
- if (iszero(z3) || isneg(z3))
- error("Mod of non-positive integer");
- if (iszero(z1) || iszero(z2) || isunit(z3)) {
- *res = _zero_;
- return;
- }
-
- /*
- * If the modulus is a single digit number, then do the result
- * cheaply. Check especially for a small power of two.
- */
- if (istiny(z3)) {
- neg = (z1.sign != z2.sign);
- digit = z3.v[0];
- if ((digit & -digit) == digit) { /* NEEDS 2'S COMP */
- prod = ((FULL) z1.v[0]) * ((FULL) z2.v[0]);
- prod &= (digit - 1);
- } else {
- z1.sign = 0;
- z2.sign = 0;
- prod = (FULL) zmodi(z1, (long) digit);
- prod *= (FULL) zmodi(z2, (long) digit);
- prod %= digit;
- }
- if (neg && prod)
- prod = digit - prod;
- itoz((long) prod, res);
- return;
- }
-
- /*
- * The modulus is more than one digit.
- * Actually do the multiply and divide if necessary.
- */
- zmul(z1, z2, &tmp);
- if (ispos(tmp) && ((tmp.len < z3.len) || ((tmp.len == z3.len) &&
- (tmp.v[tmp.len-1] < z2.v[z3.len-1]))))
- {
- *res = tmp;
- return;
- }
- zmod(tmp, z3, res);
- freeh(tmp.v);
- }
-
-
- /*
- * Square a number and then mod the result with a second number.
- * The number to be squared can be negative or out of modulo range.
- * The result will be in the range 0 to the modulus - 1.
- */
- void
- zsquaremod(z1, z2, res)
- ZVALUE z1; /* number to be squared */
- ZVALUE z2; /* number to take mod with */
- ZVALUE *res; /* result */
- {
- ZVALUE tmp;
- FULL prod;
- FULL digit;
-
- if (iszero(z2) || isneg(z2))
- error("Mod of non-positive integer");
- if (iszero(z1) || isunit(z2)) {
- *res = _zero_;
- return;
- }
-
- /*
- * If the modulus is a single digit number, then do the result
- * cheaply. Check especially for a small power of two.
- */
- if (istiny(z2)) {
- digit = z2.v[0];
- if ((digit & -digit) == digit) { /* NEEDS 2'S COMP */
- prod = (FULL) z1.v[0];
- prod = (prod * prod) & (digit - 1);
- } else {
- z1.sign = 0;
- prod = (FULL) zmodi(z1, (long) digit);
- prod = (prod * prod) % digit;
- }
- itoz((long) prod, res);
- return;
- }
-
- /*
- * The modulus is more than one digit.
- * Actually do the square and divide if necessary.
- */
- zsquare(z1, &tmp);
- if ((tmp.len < z2.len) ||
- ((tmp.len == z2.len) && (tmp.v[tmp.len-1] < z2.v[z2.len-1]))) {
- *res = tmp;
- return;
- }
- zmod(tmp, z2, res);
- freeh(tmp.v);
- }
-
-
- /*
- * Add two numbers together and then mod the result with a third number.
- * The two numbers to be added can be negative or out of modulo range.
- * The result will be in the range 0 to the modulus - 1.
- */
- static void
- zaddmod(z1, z2, z3, res)
- ZVALUE z1; /* first number to be added */
- ZVALUE z2; /* second number to be added */
- ZVALUE z3; /* number to take mod with */
- ZVALUE *res; /* result */
- {
- ZVALUE tmp;
- FULL sumdigit;
- FULL moddigit;
-
- if (iszero(z3) || isneg(z3))
- error("Mod of non-positive integer");
- if ((iszero(z1) && iszero(z2)) || isunit(z3)) {
- *res = _zero_;
- return;
- }
- if (istwo(z2)) {
- if ((z1.v[0] + z2.v[0]) & 0x1)
- *res = _one_;
- else
- *res = _zero_;
- return;
- }
- zadd(z1, z2, &tmp);
- if (isneg(tmp) || (tmp.len > z3.len)) {
- zmod(tmp, z3, res);
- freeh(tmp.v);
- return;
- }
- sumdigit = tmp.v[tmp.len - 1];
- moddigit = z3.v[z3.len - 1];
- if ((tmp.len < z3.len) || (sumdigit < moddigit)) {
- *res = tmp;
- return;
- }
- if (sumdigit < 2 * moddigit) {
- zsub(tmp, z3, res);
- freeh(tmp.v);
- return;
- }
- zmod(tmp, z2, res);
- freeh(tmp.v);
- }
-
-
- /*
- * Subtract two numbers together and then mod the result with a third number.
- * The two numbers to be subtract can be negative or out of modulo range.
- * The result will be in the range 0 to the modulus - 1.
- */
- void
- zsubmod(z1, z2, z3, res)
- ZVALUE z1; /* number to be subtracted from */
- ZVALUE z2; /* number to be subtracted */
- ZVALUE z3; /* number to take mod with */
- ZVALUE *res; /* result */
- {
- if (iszero(z3) || isneg(z3))
- error("Mod of non-positive integer");
- if (iszero(z2)) {
- zmod(z1, z3, res);
- return;
- }
- if (iszero(z1)) {
- znegmod(z2, z3, res);
- return;
- }
- if ((z1.sign == z2.sign) && (z1.len == z2.len) &&
- (z1.v[0] == z2.v[0]) && (zcmp(z1, z2) == 0)) {
- *res = _zero_;
- return;
- }
- z2.sign = !z2.sign;
- zaddmod(z1, z2, z3, res);
- }
-
-
- /*
- * Calculate the negative of a number modulo another number.
- * The number to be negated can be negative or out of modulo range.
- * The result will be in the range 0 to the modulus - 1.
- */
- static void
- znegmod(z1, z2, res)
- ZVALUE z1; /* number to take negative of */
- ZVALUE z2; /* number to take mod with */
- ZVALUE *res; /* result */
- {
- int sign;
- int cv;
-
- if (iszero(z2) || isneg(z2))
- error("Mod of non-positive integer");
- if (iszero(z1) || isunit(z2)) {
- *res = _zero_;
- return;
- }
- if (istwo(z2)) {
- if (z1.v[0] & 0x1)
- *res = _one_;
- else
- *res = _zero_;
- return;
- }
-
- /*
- * If the absolute value of the number is within the modulo range,
- * then the result is just a copy or a subtraction. Otherwise go
- * ahead and negate and reduce the result.
- */
- sign = z1.sign;
- z1.sign = 0;
- cv = zrel(z1, z2);
- if (cv == 0) {
- *res = _zero_;
- return;
- }
- if (cv < 0) {
- if (sign)
- zcopy(z1, res);
- else
- zsub(z2, z1, res);
- return;
- }
- z1.sign = !sign;
- zmod(z1, z2, res);
- }
- #endif
-
-
- /*
- * Calculate the number congruent to the given number whose absolute
- * value is minimal. The number to be reduced can be negative or out of
- * modulo range. The result will be within the range -int((modulus-1)/2)
- * to int(modulus/2) inclusive. For example, for modulus 7, numbers are
- * reduced to the range [-3, 3], and for modulus 8, numbers are reduced to
- * the range [-3, 4].
- */
- void
- zminmod(z1, z2, res)
- ZVALUE z1; /* number to find minimum congruence of */
- ZVALUE z2; /* number to take mod with */
- ZVALUE *res; /* result */
- {
- ZVALUE tmp1, tmp2;
- int sign;
- int cv;
-
- if (iszero(z2) || isneg(z2))
- error("Mod of non-positive integer");
- if (iszero(z1) || isunit(z2)) {
- *res = _zero_;
- return;
- }
- if (istwo(z2)) {
- if (isodd(z1))
- *res = _one_;
- else
- *res = _zero_;
- return;
- }
-
- /*
- * Do a quick check to see if the number is very small compared
- * to the modulus. If so, then the result is obvious.
- */
- if (z1.len < z2.len - 1) {
- zcopy(z1, res);
- return;
- }
-
- /*
- * Now make sure the input number is within the modulo range.
- * If not, then reduce it to be within range and make the
- * quick check again.
- */
- sign = z1.sign;
- z1.sign = 0;
- cv = zrel(z1, z2);
- if (cv == 0) {
- *res = _zero_;
- return;
- }
- tmp1 = z1;
- if (cv > 0) {
- z1.sign = (BOOL)sign;
- zmod(z1, z2, &tmp1);
- if (tmp1.len < z2.len - 1) {
- *res = tmp1;
- return;
- }
- sign = 0;
- }
-
- /*
- * Now calculate the difference of the modulus and the absolute
- * value of the original number. Compare the original number with
- * the difference, and return the one with the smallest absolute
- * value, with the correct sign. If the two values are equal, then
- * return the positive result.
- */
- zsub(z2, tmp1, &tmp2);
- cv = zrel(tmp1, tmp2);
- if (cv < 0) {
- freeh(tmp2.v);
- tmp1.sign = (BOOL)sign;
- if (tmp1.v == z1.v)
- zcopy(tmp1, res);
- else
- *res = tmp1;
- } else {
- if (cv)
- tmp2.sign = !sign;
- if (tmp1.v != z1.v)
- freeh(tmp1.v);
- *res = tmp2;
- }
- }
-
-
- /*
- * Compare two numbers for equality modulo a third number.
- * The two numbers to be compared can be negative or out of modulo range.
- * Returns TRUE if the numbers are not congruent, and FALSE if they are
- * congruent.
- */
- BOOL
- zcmpmod(z1, z2, z3)
- ZVALUE z1; /* first number to be compared */
- ZVALUE z2; /* second number to be compared */
- ZVALUE z3; /* modulus */
- {
- ZVALUE tmp1, tmp2, tmp3;
- FULL digit;
- LEN len;
- int cv;
-
- if (isneg(z3) || iszero(z3))
- error("Non-positive modulus in zcmpmod");
- if (istwo(z3))
- return (((z1.v[0] + z2.v[0]) & 0x1) != 0);
-
- /*
- * If the two numbers are equal, then their mods are equal.
- */
- if ((z1.sign == z2.sign) && (z1.len == z2.len) &&
- (z1.v[0] == z2.v[0]) && (zcmp(z1, z2) == 0))
- return FALSE;
-
- /*
- * If both numbers are negative, then we can make them positive.
- */
- if (isneg(z1) && isneg(z2)) {
- z1.sign = 0;
- z2.sign = 0;
- }
-
- /*
- * For small negative numbers, make them positive before comparing.
- * In any case, the resulting numbers are in tmp1 and tmp2.
- */
- tmp1 = z1;
- tmp2 = z2;
- len = z3.len;
- digit = z3.v[len - 1];
-
- if (isneg(z1) && ((z1.len < len) ||
- ((z1.len == len) && (z1.v[z1.len - 1] < digit))))
- zadd(z1, z3, &tmp1);
-
- if (isneg(z2) && ((z2.len < len) ||
- ((z2.len == len) && (z2.v[z2.len - 1] < digit))))
- zadd(z2, z3, &tmp2);
-
- /*
- * Now compare the two numbers for equality.
- * If they are equal we are all done.
- */
- if (zcmp(tmp1, tmp2) == 0) {
- if (tmp1.v != z1.v)
- freeh(tmp1.v);
- if (tmp2.v != z2.v)
- freeh(tmp2.v);
- return FALSE;
- }
-
- /*
- * They are not identical. Now if both numbers are positive
- * and less than the modulus, then they are definitely not equal.
- */
- if ((tmp1.sign == tmp2.sign) &&
- ((tmp1.len < len) || (zrel(tmp1, z3) < 0)) &&
- ((tmp2.len < len) || (zrel(tmp2, z3) < 0)))
- {
- if (tmp1.v != z1.v)
- freeh(tmp1.v);
- if (tmp2.v != z2.v)
- freeh(tmp2.v);
- return TRUE;
- }
-
- /*
- * Either one of the numbers is negative or is large.
- * So do the standard thing and subtract the two numbers.
- * Then they are equal if the result is 0 (mod z3).
- */
- zsub(tmp1, tmp2, &tmp3);
- if (tmp1.v != z1.v)
- freeh(tmp1.v);
- if (tmp2.v != z2.v)
- freeh(tmp2.v);
-
- /*
- * Compare the result with the modulus to see if it is equal to
- * or less than the modulus. If so, we know the mod result.
- */
- tmp3.sign = 0;
- cv = zrel(tmp3, z3);
- if (cv == 0) {
- freeh(tmp3.v);
- return FALSE;
- }
- if (cv < 0) {
- freeh(tmp3.v);
- return TRUE;
- }
-
- /*
- * We are forced to actually do the division.
- * The numbers are congruent if the result is zero.
- */
- zmod(tmp3, z3, &tmp1);
- freeh(tmp3.v);
- if (iszero(tmp1)) {
- freeh(tmp1.v);
- return FALSE;
- } else {
- freeh(tmp1.v);
- return TRUE;
- }
- }
-
-
- /*
- * Compute the result of raising one number to a power modulo another number.
- * That is, this computes: a^b (modulo c).
- * This calculates the result by examining the power POWBITS bits at a time,
- * using a small table of POWNUMS low powers to calculate powers for those bits,
- * and repeated squaring and multiplying by the partial powers to generate
- * the complete power. If the power being raised to is high enough, then
- * this uses the REDC algorithm to avoid doing many divisions. When using
- * REDC, multiple calls to this routine using the same modulus will be
- * slightly faster.
- */
- void
- zpowermod(z1, z2, z3, res)
- ZVALUE z1, z2, z3, *res;
- {
- HALF *hp; /* pointer to current word of the power */
- REDC *rp; /* REDC information to be used */
- ZVALUE *pp; /* pointer to low power table */
- ZVALUE ans, temp; /* calculation values */
- ZVALUE modpow; /* current small power */
- ZVALUE lowpowers[POWNUMS]; /* low powers */
- int sign; /* original sign of number */
- int curshift; /* shift value for word of power */
- HALF curhalf; /* current word of power */
- unsigned int curpow; /* current low power */
- unsigned int curbit; /* current bit of low power */
- int i;
-
- if (isneg(z3) || iszero(z3))
- error("Non-positive modulus in zpowermod");
- if (isneg(z2))
- error("Negative power in zpowermod");
-
- sign = z1.sign;
- z1.sign = 0;
-
- /*
- * Check easy cases first.
- */
- if (iszero(z1) || isunit(z3)) { /* 0^x or mod 1 */
- *res = _zero_;
- return;
- }
- if (istwo(z3)) { /* mod 2 */
- if (isodd(z1))
- *res = _one_;
- else
- *res = _zero_;
- return;
- }
- if (isunit(z1) && (!sign || iseven(z2))) {
- /* 1^x or (-1)^(2x) */
- *res = _one_;
- return;
- }
-
- /*
- * Normalize the number being raised to be non-negative and to lie
- * within the modulo range. Then check for zero or one specially.
- */
- zmod(z1, z3, &temp);
- if (iszero(temp)) {
- freeh(temp.v);
- *res = _zero_;
- return;
- }
- z1 = temp;
- if (sign) {
- zsub(z3, z1, &temp);
- freeh(z1.v);
- z1 = temp;
- }
- if (isunit(z1)) {
- freeh(z1.v);
- *res = _one_;
- return;
- }
-
- /*
- * If the modulus is odd, large enough, is not one less than an
- * exact power of two, and if the power is large enough, then use
- * the REDC algorithm. The size where this is done is configurable.
- */
- if ((z2.len > 1) && (z3.len >= _pow2_) && isodd(z3)
- && !zisallbits(z3))
- {
- if (powermodredc && zcmp(powermodredc->mod, z3)) {
- zredcfree(powermodredc);
- powermodredc = NULL;
- }
- if (powermodredc == NULL)
- powermodredc = zredcalloc(z3);
- rp = powermodredc;
- zredcencode(rp, z1, &temp);
- zredcpower(rp, temp, z2, &z1);
- freeh(temp.v);
- zredcdecode(rp, z1, res);
- freeh(z1.v);
- return;
- }
-
- /*
- * Modulus or power is small enough to perform the power raising
- * directly. Initialize the table of powers.
- */
- for (pp = &lowpowers[2]; pp < &lowpowers[POWNUMS]; pp++)
- pp->len = 0;
- lowpowers[0] = _one_;
- lowpowers[1] = z1;
- ans = _one_;
-
- hp = &z2.v[z2.len - 1];
- curhalf = *hp;
- curshift = BASEB - POWBITS;
- while (curshift && ((curhalf >> curshift) == 0))
- curshift -= POWBITS;
-
- /*
- * Calculate the result by examining the power POWBITS bits at a time,
- * and use the table of low powers at each iteration.
- */
- for (;;) {
- curpow = (curhalf >> curshift) & (POWNUMS - 1);
- pp = &lowpowers[curpow];
-
- /*
- * If the small power is not yet saved in the table, then
- * calculate it and remember it in the table for future use.
- */
- if (pp->len == 0) {
- if (curpow & 0x1)
- zcopy(z1, &modpow);
- else
- modpow = _one_;
-
- for (curbit = 0x2; curbit <= curpow; curbit *= 2) {
- pp = &lowpowers[curbit];
- if (pp->len == 0) {
- zsquare(lowpowers[curbit/2], &temp);
- zmod(temp, z3, pp);
- freeh(temp.v);
- }
- if (curbit & curpow) {
- zmul(*pp, modpow, &temp);
- freeh(modpow.v);
- zmod(temp, z3, &modpow);
- freeh(temp.v);
- }
- }
- pp = &lowpowers[curpow];
- *pp = modpow;
- }
-
- /*
- * If the power is nonzero, then accumulate the small power
- * into the result.
- */
- if (curpow) {
- zmul(ans, *pp, &temp);
- freeh(ans.v);
- zmod(temp, z3, &ans);
- freeh(temp.v);
- }
-
- /*
- * Select the next POWBITS bits of the power, if there is
- * any more to generate.
- */
- curshift -= POWBITS;
- if (curshift < 0) {
- if (hp-- == z2.v)
- break;
- curhalf = *hp;
- curshift = BASEB - POWBITS;
- }
-
- /*
- * Square the result POWBITS times to make room for the next
- * chunk of bits.
- */
- for (i = 0; i < POWBITS; i++) {
- zsquare(ans, &temp);
- freeh(ans.v);
- zmod(temp, z3, &ans);
- freeh(temp.v);
- }
- }
-
- for (pp = &lowpowers[2]; pp < &lowpowers[POWNUMS]; pp++) {
- if (pp->len)
- freeh(pp->v);
- }
- *res = ans;
- }
-
-
- /*
- * Initialize the REDC algorithm for a particular modulus,
- * returning a pointer to a structure that is used for other
- * REDC calls. An error is generated if the structure cannot
- * be allocated. The modulus must be odd and positive.
- */
- REDC *
- zredcalloc(z1)
- ZVALUE z1; /* modulus to initialize for */
- {
- REDC *rp; /* REDC information */
- ZVALUE tmp;
- long bit;
-
- if (iseven(z1) || isneg(z1))
- error("REDC requires positive odd modulus");
-
- rp = (REDC *) malloc(sizeof(REDC));
- if (rp == NULL)
- error("Cannot allocate REDC structure");
-
- /*
- * Round up the binary modulus to the next power of two
- * which is at a word boundary. Then the shift and modulo
- * operations mod the binary modulus can be done very cheaply.
- * Calculate the REDC format for the number 1 for future use.
- */
- bit = zhighbit(z1) + 1;
- if (bit % BASEB)
- bit += (BASEB - (bit % BASEB));
- zcopy(z1, &rp->mod);
- zbitvalue(bit, &tmp);
- z1.sign = 1;
- (void) zmodinv(z1, tmp, &rp->inv);
- zmod(tmp, rp->mod, &rp->one);
- freeh(tmp.v);
- rp->len = bit / BASEB;
- return rp;
- }
-
-
- /*
- * Free any numbers associated with the specified REDC structure,
- * and then the REDC structure itself.
- */
- void
- zredcfree(rp)
- REDC *rp; /* REDC information to be cleared */
- {
- freeh(rp->mod.v);
- freeh(rp->inv.v);
- freeh(rp->one.v);
- free(rp);
- }
-
-
- /*
- * Convert a normal number into the specified REDC format.
- * The number to be converted can be negative or out of modulo range.
- * The resulting number can be used for multiplying, adding, subtracting,
- * or comparing with any other such converted numbers, as if the numbers
- * were being calculated modulo the number which initialized the REDC
- * information. When the final value is unconverted, the result is the
- * same as if the usual operations were done with the original numbers.
- */
- void
- zredcencode(rp, z1, res)
- REDC *rp; /* REDC information */
- ZVALUE z1; /* number to be converted */
- ZVALUE *res; /* returned converted number */
- {
- ZVALUE tmp1, tmp2;
-
- /*
- * Handle the cases 0, 1, -1, and 2 specially since these are
- * easy to calculate. Zero transforms to zero, and the others
- * can be obtained from the precomputed REDC format for 1 since
- * addition and subtraction act normally for REDC format numbers.
- */
- if (iszero(z1)) {
- *res = _zero_;
- return;
- }
- if (isone(z1)) {
- zcopy(rp->one, res);
- return;
- }
- if (isunit(z1)) {
- zsub(rp->mod, rp->one, res);
- return;
- }
- if (istwo(z1)) {
- zadd(rp->one, rp->one, &tmp1);
- if (zrel(tmp1, rp->mod) < 0) {
- *res = tmp1;
- return;
- }
- zsub(tmp1, rp->mod, res);
- freeh(tmp1.v);
- return;
- }
-
- /*
- * Not a trivial number to convert, so do the full transformation.
- * Convert negative numbers to positive numbers before converting.
- */
- tmp1.len = 0;
- if (isneg(z1)) {
- zmod(z1, rp->mod, &tmp1);
- z1 = tmp1;
- }
- zshift(z1, rp->len * BASEB, &tmp2);
- if (tmp1.len)
- freeh(tmp1.v);
- zmod(tmp2, rp->mod, res);
- freeh(tmp2.v);
- }
-
-
- /*
- * The REDC algorithm used to convert numbers out of REDC format and also
- * used after multiplication of two REDC numbers. Using this routine
- * avoids any divides, replacing the divide by two multiplications.
- * If the numbers are very large, then these two multiplies will be
- * quicker than the divide, since dividing is harder than multiplying.
- */
- void
- zredcdecode(rp, z1, res)
- REDC *rp; /* REDC information */
- ZVALUE z1; /* number to be transformed */
- ZVALUE *res; /* returned transformed number */
- {
- ZVALUE tmp1, tmp2; /* temporaries */
- HALF *hp; /* saved pointer to tmp2 value */
-
- if (isneg(z1))
- error("Negative number for zredc");
-
- /*
- * Check first for the special values for 0 and 1 that are easy.
- */
- if (iszero(z1)) {
- *res = _zero_;
- return;
- }
- if ((z1.len == rp->one.len) && (z1.v[0] == rp->one.v[0]) &&
- (zcmp(z1, rp->one) == 0)) {
- *res = _one_;
- return;
- }
-
- /*
- * First calculate the following:
- * tmp2 = ((z1 % 2^bitnum) * inv) % 2^bitnum.
- * The mod operations can be done with no work since the bit
- * number was selected as a multiple of the word size. Just
- * reduce the sizes of the numbers as required.
- */
- tmp1 = z1;
- if (tmp1.len > rp->len)
- tmp1.len = rp->len;
- zmul(tmp1, rp->inv, &tmp2);
- if (tmp2.len > rp->len)
- tmp2.len = rp->len;
-
- /*
- * Next calculate the following:
- * res = (z1 + tmp2 * modulus) / 2^bitnum
- * The division by a power of 2 is always exact, and requires no
- * work. Just adjust the address and length of the number to do
- * the divide, but save the original pointer for freeing later.
- */
- zmul(tmp2, rp->mod, &tmp1);
- freeh(tmp2.v);
- zadd(z1, tmp1, &tmp2);
- freeh(tmp1.v);
- hp = tmp2.v;
- if (tmp2.len <= rp->len) {
- freeh(hp);
- *res = _zero_;
- return;
- }
- tmp2.v += rp->len;
- tmp2.len -= rp->len;
-
- /*
- * Finally do a final modulo by a simple subtraction if necessary.
- * This is all that is needed because the previous calculation is
- * guaranteed to always be less than twice the modulus.
- */
- if (zrel(tmp2, rp->mod) < 0)
- zcopy(tmp2, res);
- else
- zsub(tmp2, rp->mod, res);
- freeh(hp);
- }
-
-
- /*
- * Multiply two numbers in REDC format together producing a result also
- * in REDC format. If the result is converted back to a normal number,
- * then the result is the same as the modulo'd multiplication of the
- * original numbers before they were converted to REDC format. This
- * calculation is done in one of two ways, depending on the size of the
- * modulus. For large numbers, the REDC definition is used directly
- * which involves three multiplies overall. For small numbers, a
- * complicated routine is used which does the indicated multiplication
- * and the REDC algorithm at the same time to produce the result.
- */
- void
- zredcmul(rp, z1, z2, res)
- REDC *rp; /* REDC information */
- ZVALUE z1; /* first REDC number to be multiplied */
- ZVALUE z2; /* second REDC number to be multiplied */
- ZVALUE *res; /* resulting REDC number */
- {
- FULL mulb;
- FULL muln;
- HALF *h1;
- HALF *h2;
- HALF *h3;
- HALF *hd;
- HALF Ninv;
- HALF topdigit;
- LEN modlen;
- LEN len;
- LEN len2;
- SIUNION sival1;
- SIUNION sival2;
- SIUNION sival3;
- SIUNION carry;
- ZVALUE tmp;
-
- if (isneg(z1) || (z1.len > rp->mod.len) ||
- isneg(z2) || (z2.len > rp->mod.len))
- error("Negative or too large number in zredcmul");
-
- /*
- * Check for special values which we easily know the answer.
- */
- if (iszero(z1) || iszero(z2)) {
- *res = _zero_;
- return;
- }
-
- if ((z1.len == rp->one.len) && (z1.v[0] == rp->one.v[0]) &&
- (zcmp(z1, rp->one) == 0)) {
- zcopy(z2, res);
- return;
- }
-
- if ((z2.len == rp->one.len) && (z2.v[0] == rp->one.v[0]) &&
- (zcmp(z2, rp->one) == 0)) {
- zcopy(z1, res);
- return;
- }
-
- /*
- * If the size of the modulus is large, then just do the multiply,
- * followed by the two multiplies contained in the REDC routine.
- * This will be quicker than directly doing the REDC calculation
- * because of the O(N^1.585) speed of the multiplies. The size
- * of the number which this is done is configurable.
- */
- if (rp->mod.len >= _redc2_) {
- zmul(z1, z2, &tmp);
- zredcdecode(rp, tmp, res);
- freeh(tmp.v);
- return;
- }
-
- /*
- * The number is small enough to calculate by doing the O(N^2) REDC
- * algorithm directly. This algorithm performs the multiplication and
- * the reduction at the same time. Notice the obscure facts that
- * only the lowest word of the inverse value is used, and that
- * there is no shifting of the partial products as there is in a
- * normal multiply.
- */
- modlen = rp->mod.len;
- Ninv = rp->inv.v[0];
-
- /*
- * Allocate the result and clear it.
- * The size of the result will be equal to or smaller than
- * the modulus size.
- */
- res->sign = 0;
- res->len = modlen;
- res->v = alloc(modlen);
-
- hd = res->v;
- len = modlen;
- while (len--)
- *hd++ = 0;
-
- /*
- * Do this outermost loop over all the digits of z1.
- */
- h1 = z1.v;
- len = z1.len;
- while (len--) {
- /*
- * Start off with the next digit of z1, the first
- * digit of z2, and the first digit of the modulus.
- */
- mulb = (FULL) *h1++;
- h2 = z2.v;
- h3 = rp->mod.v;
- hd = res->v;
- sival1.ivalue = mulb * ((FULL) *h2++) + ((FULL) *hd++);
- muln = ((HALF) (sival1.silow * Ninv));
- sival2.ivalue = muln * ((FULL) *h3++);
- sival3.ivalue = ((FULL) sival1.silow) + ((FULL) sival2.silow);
- carry.ivalue = ((FULL) sival1.sihigh) + ((FULL) sival2.sihigh)
- + ((FULL) sival3.sihigh);
-
- /*
- * Do this innermost loop for each digit of z2, except
- * for the first digit which was just done above.
- */
- len2 = z2.len;
- while (--len2 > 0) {
- sival1.ivalue = mulb * ((FULL) *h2++);
- sival2.ivalue = muln * ((FULL) *h3++);
- sival3.ivalue = ((FULL) sival1.silow)
- + ((FULL) sival2.silow)
- + ((FULL) *hd)
- + ((FULL) carry.silow);
- carry.ivalue = ((FULL) sival1.sihigh)
- + ((FULL) sival2.sihigh)
- + ((FULL) sival3.sihigh)
- + ((FULL) carry.sihigh);
-
- hd[-1] = sival3.silow;
- hd++;
- }
-
- /*
- * Now continue the loop as necessary so the total number
- * of interations is equal to the size of the modulus.
- * This acts as if the innermost loop was repeated for
- * high digits of z2 that are zero.
- */
- len2 = modlen - z2.len;
- while (len2--) {
- sival2.ivalue = muln * ((FULL) *h3++);
- sival3.ivalue = ((FULL) sival2.silow)
- + ((FULL) *hd)
- + ((FULL) carry.silow);
- carry.ivalue = ((FULL) sival2.sihigh)
- + ((FULL) sival3.sihigh)
- + ((FULL) carry.sihigh);
-
- hd[-1] = sival3.silow;
- hd++;
- }
-
- res->v[modlen - 1] = carry.silow;
- topdigit = carry.sihigh;
- }
-
- /*
- * Now continue the loop as necessary so the total number
- * of interations is equal to the size of the modulus.
- * This acts as if the outermost loop was repeated for high
- * digits of z1 that are zero.
- */
- len = modlen - z1.len;
- while (len--) {
- /*
- * Start off with the first digit of the modulus.
- */
- h3 = rp->mod.v;
- hd = res->v;
- muln = ((HALF) (*hd * Ninv));
- sival2.ivalue = muln * ((FULL) *h3++);
- sival3.ivalue = ((FULL) *hd++) + ((FULL) sival2.silow);
- carry.ivalue = ((FULL) sival2.sihigh) + ((FULL) sival3.sihigh);
-
- /*
- * Do this innermost loop for each digit of the modulus,
- * except for the first digit which was just done above.
- */
- len2 = modlen;
- while (--len2 > 0) {
- sival2.ivalue = muln * ((FULL) *h3++);
- sival3.ivalue = ((FULL) sival2.silow)
- + ((FULL) *hd)
- + ((FULL) carry.silow);
- carry.ivalue = ((FULL) sival2.sihigh)
- + ((FULL) sival3.sihigh)
- + ((FULL) carry.sihigh);
-
- hd[-1] = sival3.silow;
- hd++;
- }
- res->v[modlen - 1] = carry.silow;
- topdigit = carry.sihigh;
- }
-
- /*
- * Determine the true size of the result, taking the top digit of
- * the current result into account. The top digit is not stored in
- * the number because it is temporary and would become zero anyway
- * after the final subtraction is done.
- */
- if (topdigit == 0) {
- len = modlen;
- hd = &res->v[len - 1];
- while ((*hd == 0) && (len > 1)) {
- hd--;
- len--;
- }
- res->len = len;
- }
-
- /*
- * Compare the result with the modulus.
- * If it is less than the modulus, then the calculation is complete.
- */
- if ((topdigit == 0) && ((len < modlen)
- || (res->v[len - 1] < rp->mod.v[len - 1])
- || (zrel(*res, rp->mod) < 0)))
- return;
-
- /*
- * Do a subtraction to reduce the result to a value less than
- * the modulus. The REDC algorithm guarantees that a single subtract
- * is all that is needed. Ignore any borrowing from the possible
- * highest word of the current result because that would affect
- * only the top digit value that was not stored and would become
- * zero anyway.
- */
- carry.ivalue = 0;
- h1 = rp->mod.v;
- hd = res->v;
- len = modlen;
- while (len--) {
- carry.ivalue = BASE1 - ((FULL) *hd) + ((FULL) *h1++)
- + ((FULL) carry.silow);
- *hd++ = BASE1 - carry.silow;
- carry.silow = carry.sihigh;
- }
-
- /*
- * Now finally recompute the size of the result.
- */
- len = modlen;
- hd = &res->v[len - 1];
- while ((*hd == 0) && (len > 1)) {
- hd--;
- len--;
- }
- res->len = len;
- }
-
-
- /*
- * Square a number in REDC format producing a result also in REDC format.
- */
- void
- zredcsquare(rp, z1, res)
- REDC *rp; /* REDC information */
- ZVALUE z1; /* REDC number to be squared */
- ZVALUE *res; /* resulting REDC number */
- {
- ZVALUE tmp;
-
- if (isneg(z1))
- error("Negative number in zredcsquare");
- if (iszero(z1)) {
- *res = _zero_;
- return;
- }
- if ((z1.len == rp->one.len) && (z1.v[0] == rp->one.v[0]) &&
- (zcmp(z1, rp->one) == 0)) {
- zcopy(z1, res);
- return;
- }
-
- /*
- * If the modulus is small enough, then call the multiply
- * routine to produce the result. Otherwise call the O(N^1.585)
- * routines to get the answer.
- */
- if (rp->mod.len < _redc2_) {
- zredcmul(rp, z1, z1, res);
- return;
- }
- zsquare(z1, &tmp);
- zredcdecode(rp, tmp, res);
- freeh(tmp.v);
- }
-
-
- /*
- * Compute the result of raising a REDC format number to a power.
- * The result is within the range 0 to the modulus - 1.
- * This calculates the result by examining the power POWBITS bits at a time,
- * using a small table of POWNUMS low powers to calculate powers for those bits,
- * and repeated squaring and multiplying by the partial powers to generate
- * the complete power.
- */
- void
- zredcpower(rp, z1, z2, res)
- REDC *rp; /* REDC information */
- ZVALUE z1; /* REDC number to be raised */
- ZVALUE z2; /* normal number to raise number to */
- ZVALUE *res; /* result */
- {
- HALF *hp; /* pointer to current word of the power */
- ZVALUE *pp; /* pointer to low power table */
- ZVALUE ans, temp; /* calculation values */
- ZVALUE modpow; /* current small power */
- ZVALUE lowpowers[POWNUMS]; /* low powers */
- int curshift; /* shift value for word of power */
- HALF curhalf; /* current word of power */
- unsigned int curpow; /* current low power */
- unsigned int curbit; /* current bit of low power */
- int i;
-
- if (isneg(z1))
- error("Negative number in zredcpower");
- if (isneg(z2))
- error("Negative power in zredcpower");
-
- /*
- * Check for zero or the REDC format for one.
- */
- if (iszero(z1) || isunit(rp->mod)) {
- *res = _zero_;
- return;
- }
- if (zcmp(z1, rp->one) == 0) {
- zcopy(rp->one, res);
- return;
- }
-
- /*
- * See if the number being raised is the REDC format for -1.
- * If so, then the answer is the REDC format for one or minus one.
- * To do this check, calculate the REDC format for -1.
- */
- if (((HALF)(z1.v[0] + rp->one.v[0])) == rp->mod.v[0]) {
- zsub(rp->mod, rp->one, &temp);
- if (zcmp(z1, temp) == 0) {
- if (isodd(z2)) {
- *res = temp;
- return;
- }
- freeh(temp.v);
- zcopy(rp->one, res);
- return;
- }
- freeh(temp.v);
- }
-
- for (pp = &lowpowers[2]; pp < &lowpowers[POWNUMS]; pp++)
- pp->len = 0;
- zcopy(rp->one, &lowpowers[0]);
- zcopy(z1, &lowpowers[1]);
- zcopy(rp->one, &ans);
-
- hp = &z2.v[z2.len - 1];
- curhalf = *hp;
- curshift = BASEB - POWBITS;
- while (curshift && ((curhalf >> curshift) == 0))
- curshift -= POWBITS;
-
- /*
- * Calculate the result by examining the power POWBITS bits at a time,
- * and use the table of low powers at each iteration.
- */
- for (;;) {
- curpow = (curhalf >> curshift) & (POWNUMS - 1);
- pp = &lowpowers[curpow];
-
- /*
- * If the small power is not yet saved in the table, then
- * calculate it and remember it in the table for future use.
- */
- if (pp->len == 0) {
- if (curpow & 0x1)
- zcopy(z1, &modpow);
- else
- zcopy(rp->one, &modpow);
-
- for (curbit = 0x2; curbit <= curpow; curbit *= 2) {
- pp = &lowpowers[curbit];
- if (pp->len == 0)
- zredcsquare(rp, lowpowers[curbit/2],
- pp);
- if (curbit & curpow) {
- zredcmul(rp, *pp, modpow, &temp);
- freeh(modpow.v);
- modpow = temp;
- }
- }
- pp = &lowpowers[curpow];
- *pp = modpow;
- }
-
- /*
- * If the power is nonzero, then accumulate the small power
- * into the result.
- */
- if (curpow) {
- zredcmul(rp, ans, *pp, &temp);
- freeh(ans.v);
- ans = temp;
- }
-
- /*
- * Select the next POWBITS bits of the power, if there is
- * any more to generate.
- */
- curshift -= POWBITS;
- if (curshift < 0) {
- if (hp-- == z2.v)
- break;
- curhalf = *hp;
- curshift = BASEB - POWBITS;
- }
-
- /*
- * Square the result POWBITS times to make room for the next
- * chunk of bits.
- */
- for (i = 0; i < POWBITS; i++) {
- zredcsquare(rp, ans, &temp);
- freeh(ans.v);
- ans = temp;
- }
- }
-
- for (pp = lowpowers; pp < &lowpowers[POWNUMS]; pp++) {
- if (pp->len)
- freeh(pp->v);
- }
- *res = ans;
- }
-
- /* END CODE */
-